#include "gui.h"

//重载new和delete
void *operator new(size_t size)
{
    return pvPortMalloc(size);
}

void operator delete(void *pointer)
{
    vPortFree(pointer);
}

void (*fill_srcee_)(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t *data);

window *window::main_window_ = nullptr;
window *window::desktop_window_ = nullptr;
uint8_t window::creat_main_window_flag_ = false;
window *window::focus_window_ = nullptr;
uint8_t window::mouse_status_ = false;
int16_t window::mouse_x_ = 0, window::mouse_y_ = 0;

struct
{
    uint16_t min_x_ = 0, min_y_ = 0, max_x_ = 0, max_y_ = 0;
    uint8_t usered_flag_ = false;
} min_max_pixel_coordinate_;

window::window(int16_t x, int16_t y, uint16_t width, uint16_t height) : x_(x), y_(y), width_(width > 0 ? width : 1), height_(height > 0 ? height : 1)
{
    if (!creat_main_window_flag_)
    {
        creat_main_window_flag_ = true;
        desktop_window_ = this;

        //建立主窗口
        main_window_ = new window;
        connect(main_window_);

        x_ = y_ = origin_;
        width_ = main_window_width_;
        height_ = main_window_height_;
    }

    if (color_type_ == _1bit_color_type)
        display_buffer_ = new uint8_t[width_ * (height_ / 8 + (height_ % 8 ? 1 : 0))];
    else
        display_buffer_ = new uint16_t[width_ * height_];

    fill(0);
    draw_frame();
};

window::~window()
{
    if (color_type_ == _1bit_color_type)
        delete[](uint8_t *) display_buffer_;
    else
        delete[](uint16_t *) display_buffer_;

    if (this == desktop_window_)
        delete main_window_;
}

void window::connect(window *parent) //连接父窗口。跟在父窗口的最后一个子窗口右边
{
    if (this == main_window_ || (this == desktop_window_ && parent != main_window_) || parent == this || parent == 0 || parent_) //不能连接自己
        return;

    parent_ = parent;                //parent作为自己的父窗口
    previous_ = parent->last_child_; //将父窗口的最后一个child作为自己的上一个窗口

    if (parent_->last_child_)               //如果父窗口有最后一个child
        parent_->last_child_->next_ = this; //把自己作为该窗口的下一个同级窗口
    parent_->last_child_ = this;            //把自己作为父窗口的last_child_

    if (parent_->first_child_ == nullptr) //如果父窗口的第一个child为空
        parent_->first_child_ = this;     //则把自己作为父窗口的第一个child
}

void window::disconnect(void)
{
    if (this == main_window_ || this == desktop_window_ || parent_ == nullptr)
        return;
    //处理其它窗口
    if (previous_)
        previous_->next_ = next_;
    if (next_)
        next_->previous_ = previous_;

    if (parent_->first_child_ == this)
        parent_->first_child_ = next_;

    if (parent_->last_child_ == this)
        parent_->last_child_ = previous_;

    //处理自己的窗口
    previous_ = nullptr, next_ = nullptr;
    parent_ = nullptr;
}

void window::set_fill_srceen_pt(void (*fill_srcee)(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t *data))
{
    fill_srcee_ = fill_srcee;
}

void window::updata_all_window()
{
    if (!desktop_window_)
        return;
    desktop_window_->updata_my_window(origin_, origin_, main_window_width_, main_window_height_, origin_, origin_);

    word::draw_string(main_window_, "+", mouse_x_, mouse_y_); //绘制鼠标

    //向屏幕绘图
    if(fill_srcee_)
        fill_srcee_(min_max_pixel_coordinate_.min_x_, min_max_pixel_coordinate_.min_y_, 
            min_max_pixel_coordinate_.max_x_ - min_max_pixel_coordinate_.min_x_ + 1, min_max_pixel_coordinate_.max_y_ - min_max_pixel_coordinate_.min_y_ + 1,
            (uint16_t*)(main_window_->display_buffer_) + min_max_pixel_coordinate_.min_y_ * main_window_width_ + min_max_pixel_coordinate_.min_x_);
}

//父窗口的可显示区域相对于父窗口的位置，自己窗口的绝对位置。
void window::updata_my_window(uint16_t display_x, uint16_t display_y, uint16_t display_width, uint16_t display_height, uint16_t x, uint16_t y) //窗口的绝对地址
{
    uint16_t p_x, p_y, my_x, my_y, d_width, d_height; //自己窗口的可显示区域相对于父窗口的坐标，在自己窗口里的起始坐标，可显示区域的长宽
    if (calculate_overlap_range(display_x, display_y, display_width, display_height, p_x, p_y, my_x, my_y, d_width, d_height))
    {
        for (uint16_t h = 0; h < d_height; ++h)
        {
            for (uint16_t w = 0; w < d_width; ++w)
                draw_main_window_pixel(x - x_ + p_x + w, y - y_ + p_y + h, get_pixel(w + my_x, h + my_y));
        }

        if (first_child_)
            first_child_->updata_my_window(my_x, my_y, d_width, d_height, first_child_->get_x() + x, first_child_->get_y() + y);
    }

    if (next_)
        next_->updata_my_window(display_x, display_y, display_width, display_height, x - x_ + next_->get_x(), y - y_ + next_->get_y());
}

uint8_t window::calculate_overlap_range(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t &p_sta_x, uint16_t &p_sta_y, uint16_t &my_sta_x, uint16_t &my_sta_y, uint16_t &my_width, uint16_t &my_height)
{
    if (x_ > x + width - 1 || get_x2() < x || y_ > y + height - 1 || get_y2() < y)
        return false;

    uint16_t x_r_min = (x + width - 1) > get_x2() ? get_x2() : (x + width - 1);
    uint16_t x_l_max = x > x_ ? x : x_;
    uint16_t y_t_max = y > y_ ? y : y_;
    uint16_t y_b_min = (y + height - 1) > get_y2() ? get_y2() : (y + height - 1);

    my_width = x_r_min - x_l_max + 1;
    my_height = y_b_min - y_t_max + 1;

    p_sta_x = x_l_max;
    p_sta_y = y_t_max;

    my_sta_x = x_l_max - x_;
    my_sta_y = y_t_max - y_;

    return true;
}

void window::in_which_window(uint16_t x, uint16_t y, uint8_t status, uint8_t pattern)
{
    window *win;
    static uint8_t mouse_previous_status = 0;
    //将鼠标限制在主窗口范围内
    if (!pattern)
    {
        if (x > 128) //左移
            --mouse_x_;
        else if (y > 128) //上移
            --mouse_y_;
        else if (x < 128 && y == 0) //右移
            ++mouse_x_;
        else //下移
            ++mouse_y_;
    }
    else
        mouse_x_ = x, mouse_y_ = y;

    mouse_x_ = mouse_x_ <= 0 ? 0 : mouse_x_ < main_window_width_ ? mouse_x_ : main_window_width_ - 1;
    mouse_y_ = mouse_y_ <= 0 ? 0 : mouse_y_ < main_window_height_ ? mouse_y_ : main_window_height_ - 1;

    mouse_status_ = status;

    win = desktop_window_->get_focus_in_window(mouse_x_, mouse_y_);
    //把获得焦点的窗口的终父窗口（直接连接在主窗口下面的），放到链表最右边，使其能覆盖在其它的窗口上
    if (status == true && mouse_previous_status == false) //当鼠标被按下，就会鼠标所在的窗口作为焦点窗口
    {
        focus_window_ = win;                 //作为焦点窗口。只有松开鼠标，才能设置下一个焦点窗口
        move_focus_initial_window_to_last(); //将它所在的终父窗口作为主窗口下的顶端窗口
    }
    mouse_previous_status = status;
    run_action(); //调用焦点窗口的动作函数
}

window *window::get_focus_in_window(int16_t x, int16_t y)
{
    static window *hit;
    hit = nullptr;

    if (x >= x_ && x <= get_x2() && y >= y_ && y <= get_y2()) //只有点在自己窗口内才会去子窗口或者自我判断
    {
        if (last_child_) //去往最后一个子窗口
        {
            hit = last_child_->get_focus_in_window(x - x_, y - y_); //换算成相对于父窗口的坐标
            if (hit != nullptr)                                     //如果焦点在子窗口上则返回获得焦点的窗口，否则进入自我判断
                return hit;
        }

        if (x >= x_ && x <= get_x2() && y >= y_ && y <= get_y2())
        {
            hit = this;
            return hit;
        }
    }

    if (previous_) //去往上一个同级窗口
    {
        hit = previous_->get_focus_in_window(x, y); //仍使用相对于父窗口的坐标
        if (hit != nullptr)
            return hit;
    }

    return hit;
}

void window::move_focus_initial_window_to_last()
{
    if (!focus_window_ || focus_window_ == desktop_window_)
        return;

    window *win = focus_window_;
    while (win->parent_ != desktop_window_)
        win = win->parent_;

    if (win->widget_type_ != window_type)
        return;

    win->disconnect();
    win->connect(desktop_window_);
}

void window::draw_main_window_pixel(uint16_t x, uint16_t y, uint32_t color)
{
    if (x > main_window_width_ - 1 || y > main_window_height_ - 1)
        return;
    //记录x，y的最大和最小位置
    if (min_max_pixel_coordinate_.usered_flag_ == false)
    {
        min_max_pixel_coordinate_.usered_flag_ = true;
        min_max_pixel_coordinate_.min_x_ = min_max_pixel_coordinate_.max_x_ = x;
        min_max_pixel_coordinate_.min_y_ = min_max_pixel_coordinate_.max_y_ = y;
    }
    else
    {
        if (x < min_max_pixel_coordinate_.min_x_)
            min_max_pixel_coordinate_.min_x_ = x;
        if (y < min_max_pixel_coordinate_.min_y_)
            min_max_pixel_coordinate_.min_y_ = y;
        if (x > min_max_pixel_coordinate_.max_x_)
            min_max_pixel_coordinate_.max_x_ = x;
        if (y > min_max_pixel_coordinate_.max_y_)
            min_max_pixel_coordinate_.max_y_ = y;
    }

    if (color_type_ == _1bit_color_type)
    {
        uint16_t addr = y / 8 * main_window_width_ + x;
        uint8_t src = ((uint8_t *)(main_window_->display_buffer_))[addr];
        color ? src = (0x01 << (y % 8)) | src : src = (~(0x01 << (y % 8))) & src;
        ((uint8_t *)(main_window_->display_buffer_))[addr] = src;
    }
    else
        ((uint16_t *)(main_window_->display_buffer_))[y * main_window_width_ + x] = color;
}

void window::draw_frame()
{
    for (uint16_t i = 0; i < width_; ++i)
    {
        draw_pixel(i, 0, 0xffff);
        draw_pixel(i, height_ - 1, 0xffff);
    }
    for (uint16_t i = 0; i < height_; ++i)
    {
        draw_pixel(0, i, 0xffff);
        draw_pixel(width_ - 1, i, 0xffff);
    }
}

void window::fill(uint32_t color)
{
    for (uint16_t x = 0; x < width_; ++x)
    {
        for (uint16_t y = 0; y < height_; ++y)
        {
            draw_pixel(x, y, color);
        }
    }
}

void window::get_mouse_relative_me_xy(int16_t &x, int16_t &y) //获取鼠标相对于自己窗口的位置
{
    x = mouse_x_, y = mouse_y_;
    window *win = this;
    while (win != desktop_window_)
    {
        x -= win->x_;
        y -= win->y_;
        win = win->parent_;
    }
}

void window::get_window_absolute_coordinates(int16_t &x, int16_t &y) //获取自己窗口的绝对位置
{
    x = 0, y = 0;
    window *win = this;
    while (win != desktop_window_)
    {
        x += win->x_;
        y += win->y_;
        win = win->parent_;
    }
}

void window::run_action(void)
{
    if (focus_window_ && focus_window_->derived_)
        focus_window_->derived_->action();
}

button::button(window *win, const char *str, int16_t x, int16_t y, uint16_t width, uint16_t height) : window(x, y, width, height)
{
    set_derived(this); //将派生类指针传递给window基类
    connect(win);
    set_widget_type(other_type);

    if (str)
        word::draw_string(this, str, 1, 1);
}

void button::action(void) //更新按钮的状态
{
    if (get_mouse_status())
        status = true;
    else
        status = false;
}

toolbar::toolbar(window *win, uint16_t height, const char *str) : window(0, 0, win->get_width(), height)
{
    set_derived(this); //将派生类指针传递给window基类
    connect(win);
    set_widget_type(other_type);

    if (str)
        word::draw_string(this, str, 1, 1);
}

void toolbar::action(void) //移动父窗口
{
    static int16_t ref_pt_x, ref_pt_y; //参考点。不得移出父窗口的父窗口
    static uint8_t status;
    if (!get_mouse_status())
    {
        status = 0;
        return;
    }
    if (status == 0)
        get_mouse_relative_me_xy(ref_pt_x, ref_pt_y); //获取鼠标相对于自己的位置，并将那点作为参考点
    status = 1;

    int16_t pt_x, pt_y;
    get_parent()->get_parent()->get_mouse_relative_me_xy(pt_x, pt_y); //鼠标相对于父父窗口的位置

    //将点限制在父父窗口里
    pt_x = pt_x >= get_parent()->get_parent()->get_width() - 1 ? get_parent()->get_parent()->get_width() - 1 : pt_x < 0 ? 0 : pt_x;
    pt_y = pt_y >= get_parent()->get_parent()->get_height() - 1 ? get_parent()->get_parent()->get_height() - 1 : pt_y < 0 ? 0 : pt_y;

    //计算点与参考点的差值
    pt_x -= (ref_pt_x + get_parent()->get_x());
    pt_y -= (ref_pt_y + get_parent()->get_y());

    get_parent()->move_window(get_parent()->get_x() + pt_x, get_parent()->get_y() + pt_y);
}

windowss::windowss(window *win, int16_t x, int16_t y, uint16_t width, uint16_t height, uint8_t creat_toolbar) : window(x, y, width, height)
{
    set_derived(this); //将派生类指针传递给window基类
    connect(win);

    if (this != desktop_window_->get_derived() && creat_toolbar)
    {
        draw_window_ = new window(origin_, toolbar_height_, width, height > 0 ? height : 1); //如果有工具栏，将重新创建一个窗口给用户使用
        draw_window_->connect(this);
        toolbar_ = new toolbar(this, toolbar_height_);
    }
}

windowss::~windowss()
{
    if (draw_window_)
        delete draw_window_;
    if (toolbar_)
        delete toolbar_;
}